package gui;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JPanel;

import logica.Analizador;
import arbol.BinaryTreeNode;
import arbol.TreeNode;

/**
 * @version 1.0 Octubre-2013 
 * Esta clase es la que contiene los atributos de BinaryTreePanel.java y 
 * maneja su información
 */
@SuppressWarnings("serial")
public class TreePanel extends JPanel 
{
	/**
	 * Atributo que contiene el valor de tree dentro de la clase
	*/
	private TreeNode<String> tree;
	
	/**
	 * Atributo que contiene el valor de gridwidth dentro de la clase
	*/
	private int gridwidth;
	
	/**
	 * Atributo que contiene el valor de gridheight dentro de la clase
	*/
	private int gridheight;

	/**
	 * Stores the pixel values for each node in the tree.
	 */
	private Map<BinaryTreeNode<?>, Point> coordinates =
			new HashMap<BinaryTreeNode<?>, Point>();

	/**
	 * Constructs a panel, saving the tree and drawing parameters.
	 */
	public TreePanel(int gridwidth, int gridheight) {
		this.gridwidth = gridwidth;
		this.gridheight = gridheight;
		initGUI();
	}

	/**
	 * Changes the tree rendered by this panel.
	 */
	public void setTree(TreeNode<String> root) {
		tree = root;
		repaint();
	}

    /**
     * Inicializador de los componentes graficos
     */
    private void initGUI() {
    	setBackground(Color.WHITE);
        setLayout(null);
    }
	
	/**
	 * Draws the tree in the panel.  First it computes the coordinates
	 * of all the nodes with an inorder traversal, then draws them
	 * with a postorder traversal.
	 */
	@Override
	public void paint(final Graphics g) 
	{
		super.paint(g);
		
		// Si es null no pinta nada
		if (tree == null)
			return;
		
		tree.traverseInorder(new BinaryTreeNode.Visitor() {
			private int x = gridwidth;
			@SuppressWarnings("rawtypes")
			public void visit(BinaryTreeNode node) {
				@SuppressWarnings("unchecked")
				TreeNode<String> nodo = (TreeNode<String>) node;
				
				// Si es negado le aumento 40 en Y
				if(nodo.isNegado())
					coordinates.put(node, new Point(x, 40 + gridheight * (depth(node)+1)));
				else
					coordinates.put(node, new Point(x, gridheight * (depth(node)+1)));
				
				x += gridwidth;
			}
		});
		
		tree.traversePostorder(new BinaryTreeNode.Visitor() {
			@SuppressWarnings("rawtypes")
			public void visit(BinaryTreeNode node) {
				FontMetrics fm = g.getFontMetrics();
				
				// Caracter que contiene el nodo
				String data = node.getData().toString();
				
				// Punto donde fue ubicado el nodo
				Point center = (Point) coordinates.get(node);
				
				@SuppressWarnings("unchecked")
				TreeNode<String> nodo = (TreeNode) node;
				
				if (node.getParent() != null) {
					Point parentPoint = (Point)coordinates.get(node.getParent());
					g.setColor(Color.black);

					// Si es negado dibujo la linea con una diferencia de 40 en y
					if(nodo.isNegado())
						g.drawLine(center.x, center.y - 40, parentPoint.x, parentPoint.y);
					else
						g.drawLine(center.x, center.y, parentPoint.x, parentPoint.y);
				}
				
				if(nodo.isNegado())
				{
					Rectangle r1 = fm.getStringBounds(Analizador.NEGACION + "", g).getBounds();
					
					r1.setLocation(center.x - r1.width/2, center.y - r1.height/2);
					
					// Dibujo la linea
					g.setColor(Color.black);
					g.drawLine(center.x + 6, center.y - 40, r1.x + 9, r1.y);
					
					// Circulo amarillo
					g.setColor(Color.yellow);
					g.fillOval(r1.x, r1.y -40, 20, 20);
					
					// Circulo blanco que generara el contorno amarillo
					g.setColor(Color.white);
					g.fillOval(r1.x+2, r1.y+2 -40, 16, 16);
					
					// Escribe la letra proposicional
					g.setColor(Color.black);
					g.drawString(Analizador.NEGACION + "", r1.x + 6 , r1.y + 15 -40);
				}
				
				Rectangle r = fm.getStringBounds(data, g).getBounds();
				r.setLocation(center.x - r.width/2, center.y - r.height/2);
				
				// Circulo amarillo
				g.setColor(Color.yellow);
				g.fillOval(r.x , r.y , 20, 20);
				
				// Circulo blanco que generara el contorno amarillo
				g.setColor(Color.white);
				g.fillOval(r.x+2 , r.y+2 , 16, 16);
				
				// Escribe la letra proposicional
				g.setColor(Color.black);
				g.drawString(data, r.x + 6 , r.y + 15);
			}
		});
	}

	/**
	 * Returns a color for the node.  If the node is of a class with a
	 * field called "color", and that field currently contains a
	 * non-null value, then that value is returned.  Otherwise
	 * a default color of yellow is returned.
	 */
	Color getNodeColor(BinaryTreeNode<?> node) {
		try {
			Field field = node.getClass().getDeclaredField("color");
			return (Color)field.get(node);
		} catch (Exception e) {
			return Color.yellow;
		}
	}

	private int depth(BinaryTreeNode<?> node) {
		return (node.getParent() == null) ? 0 : 1 + depth(node.getParent());
	}
}